package com.iotechn.unimall.app.api.seckill;


import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.baomidou.mybatisplus.mapper.Wrapper;
import com.iotechn.unimall.app.api.category.CategoryService;
import com.iotechn.unimall.app.api.distribution.DistributionService;
import com.iotechn.unimall.app.api.goods.GoodsService;
import com.iotechn.unimall.biz.service.appriaise.AppraiseBizService;
import com.iotechn.unimall.biz.service.category.CategoryBizService;
import com.iotechn.unimall.biz.service.collect.CollectBizService;
import com.iotechn.unimall.biz.service.distribution.DistributionBizService;
import com.iotechn.unimall.biz.service.footpring.FootprintBizService;
import com.iotechn.unimall.biz.service.freight.FreightBizService;
import com.iotechn.unimall.biz.service.seckill.SeckillSkuBizService;
import com.iotechn.unimall.biz.service.seckill.SeckillSpuBizService;
import com.iotechn.unimall.core.Const;
import com.iotechn.unimall.core.exception.*;
import com.iotechn.unimall.core.util.DateUtils;
import com.iotechn.unimall.core.util.GeneratorUtil;
import com.iotechn.unimall.core.util.MoneyUtils;
import com.iotechn.unimall.data.component.CacheComponent;
import com.iotechn.unimall.data.component.LockComponent;
import com.iotechn.unimall.data.domain.*;
import com.iotechn.unimall.data.dto.DistributionDTO;
import com.iotechn.unimall.data.dto.IntegralIndexDataDTO;
import com.iotechn.unimall.data.dto.freight.FreightTemplateDTO;
import com.iotechn.unimall.data.dto.goods.SpuDTO;
import com.iotechn.unimall.data.dto.order.OrderRequestDTO;
import com.iotechn.unimall.data.dto.order.OrderRequestSkuDTO;
import com.iotechn.unimall.data.dto.seckill.*;
import com.iotechn.unimall.data.enums.*;
import com.iotechn.unimall.data.mapper.*;
import com.iotechn.unimall.data.model.Page;
import com.iotechn.unimall.data.util.SessionUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.DigestUtils;
import org.springframework.util.StringUtils;

import java.rmi.ServerException;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @auther TyCoding
 * @date 2018/10/6
 */
@Service
public class SeckillServiceImpl implements SeckillService {

    private static final String TAKE_SECKILLORDER_LOCK = "TAKE_SECKILLORDER_";
    private static final String TAKE_SECKILLSKU_LOCK = "TAKE_SECKILLSKU_";
    private static final String SECKILL_SPU_PREFIX = "SECKILL_SPU_";
    //设置盐值字符串，随便定义，用于混淆MD5值
    private final String salt = "sjajaspu-i-2jrfm;sd";
    //设置秒杀redis缓存的key
    private final String key = "seckill";
    private Logger logger = LoggerFactory.getLogger(this.getClass());
    @Value("${com.iotechn.unimall.machine-no}")
    private String MACHINE_NO;
    @Value("${com.iotechn.unimall.env}")
    private String ENV;
    @Autowired
    private SeckillSpuMapper seckillSpuMapper;
    @Autowired
    private SeckillSkuMapper seckillSkuMapper;
    @Autowired
    private SeckillOrderMapper seckillOrderMapper;
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private GoodsService goodsBizService;
    @Autowired
    private SeckillSkuBizService SeckillSkuBizService;
    @Autowired
    private CacheComponent cacheComponent;
    @Autowired
    private CollectBizService collectBizService;
    @Autowired
    private AppraiseBizService appraiseBizService;
    @Autowired
    private FootprintBizService footprintBizService;
    @Autowired
    private SkuMapper skuMapper;
    @Autowired
    private SkuColonelMapper skuColonelMapper;
    @Autowired
    private SpuMapper spuMapper;
    @Autowired
    private ImgMapper imgMapper;
    @Autowired
    private CategoryBizService categoryBizService;
    @Autowired
    private SpuAttributeMapper spuAttributeMapper;
    @Autowired
    private FreightBizService freightBizService;
    @Autowired
    private CollectMapper collectMapper;
    @Autowired
    private LockComponent lockComponent;

    @Autowired
    private CategoryService categoryService;
    @Autowired
    private UserCouponMapper userCouponMapper;
    @Autowired
    private DistributionService distributionService;
    @Autowired
    private DistributionBizService distributionBizService;
    @Autowired
    private AddressMapper addressMapper;
    @Autowired
    private OrderMapper orderMapper;
    @Autowired
    private OrderSkuMapper orderSkuMapper;
    @Autowired
    private CategoryMapper categoryMapper;
    @Autowired
    private CartMapper cartMapper;
    @Autowired
    private SeckillOrderSkuMapper seckillOrderSkuMapper;
    @Autowired
    private SeckillUserSkuMapper seckillUserSkuMapper;
    @Autowired
    private SeckillSpuBizService seckillSpuBizService;

    static <E extends Exception> void doThrow(Exception e) throws E {
        throw (E) e;
    }

    @Override
    public Page<SeckillSpuDTO> getSeckillPage(Integer pageNo, Integer pageSize) throws ServerException {
        Integer count = 0;
        List<SeckillSpuDTO> seckillSpuPage = new ArrayList<>();
        try {
            count = seckillSpuMapper.selectCount(null);
            seckillSpuPage = seckillSpuMapper.getPage((pageNo - 1) * pageSize, pageSize);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return new Page<>(seckillSpuPage, pageNo, pageSize, count);
    }

    /**
     * 获取当天的秒杀列表
     * 或  某一时间段内的秒杀列表
     *
     * @param pageNo
     * @param pageSize
     * @return
     * @throws ServerException
     */
    @Override
    public Page<Map<String, Object>> getSeckillPageToday(Integer pageNo, Integer pageSize, Date gmtStart) throws ServerException {
        Integer count = 0;
        List<Map<String, Object>> result = new ArrayList<>();
        try {
            count = seckillSpuMapper.getPageTodayCount();
            result = new ArrayList<>();
            List<Date> gmtStartList = seckillSpuMapper.getGmtStartList();
            Map<String, Object> map = new LinkedHashMap<>();
            //查询当天的
//        for (Date date : gmtStartList
//        ) {
//            List<SeckillSpuDTO> pageToday = seckillSpuMapper.getPageToday((pageNo - 1) * pageSize, pageSize, date);
//            map.put(DateUtils.getDateMAndH(date), pageToday);
//        }
            //查询所有秒杀活动
            List<SeckillSpuDTO> pageToday = seckillSpuMapper.getPageToday((pageNo - 1) * pageSize, pageSize, null);
            map.put(" ", pageToday);
            result.add(map);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return new Page<Map<String, Object>>(result, pageNo, pageSize, count);
    }

    /**
     * 获取当天秒杀活动数量
     *
     * @return
     */
    @Override
    public Integer getSeckillCountToday() throws ServerException {
        Integer count = 0;
        try {
            count = seckillSpuMapper.selectCount(null);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return count;
    }

    /**
     * 获取当天所有整点时间
     *
     * @return
     */
    @Override
    public List<Date> getGmtStartList() throws ServerException {
        List<Date> gmtStartList = new ArrayList<>();
        try {
            gmtStartList = seckillSpuMapper.getGmtStartList();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return gmtStartList;
    }

    @Override
    public Map<String, Object> latelySeckillList() throws ServerException {
        Map<String, Object> resultMap = new HashMap<>();
        try {
            resultMap = new HashMap<>();
            List<SeckillSpuDTO> seckillSpuDTOS = seckillSpuMapper.latelySeckillList();
            if (seckillSpuDTOS.size() < 1) {
                //判断当前时间是否有 秒杀活动
                Integer pageTodayCount = seckillSpuMapper.getPageTodayCount();
                Integer num=0;
                Integer count=1;
                if(pageTodayCount > 1 ){
                    num=pageTodayCount-2;
                    count=2;
                }
                //选择最后一个开启的秒撒谎活动
                if (pageTodayCount > 0) {
                    seckillSpuDTOS = seckillSpuMapper.startLastSeckillList(num, count);
                    SeckillSpuDTO seckillSpuDTO =seckillSpuDTOS.get(0);
                    CategoryDO categoryDO = categoryMapper.selectById(seckillSpuDTO.getCategoryId());
                    Double spuSeckillPrice = seckillSpuDTO.getSpuSeckillPrice().doubleValue();
                    Double OriginalPrice = seckillSpuDTO.getOriginalPrice().doubleValue();
                    Double discount = MoneyUtils.div(spuSeckillPrice, OriginalPrice, 1);
                    resultMap.put("startTime", seckillSpuDTO.getGmtStart());
                    resultMap.put("endtTime", seckillSpuDTO.getGmtEnd());
                    resultMap.put("seckillList", seckillSpuDTOS);
                    resultMap.put("seckillText", "每日限时秒杀  " + categoryDO.getTitle() + discount + "折起");
                    return resultMap;
                }
                if (pageTodayCount < 1) {
                    return null;
                }

            }
            SeckillSpuDTO seckillSpuDTO = seckillSpuDTOS.get(0);
            Double spuSeckillPrice = seckillSpuDTO.getSpuSeckillPrice().doubleValue();
            Double OriginalPrice = seckillSpuDTO.getOriginalPrice().doubleValue();
            Double discount = MoneyUtils.div(spuSeckillPrice, OriginalPrice, 1);
            CategoryDO categoryDO = categoryMapper.selectById(seckillSpuDTO.getCategoryId());

            resultMap.put("startTime", seckillSpuDTO.getGmtStart());
            resultMap.put("endtTime", seckillSpuDTO.getGmtEnd());
            resultMap.put("seckillText", "每日限时秒杀  " + categoryDO.getTitle() + discount + "折起");
            resultMap.put("seckillList", seckillSpuDTOS);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return resultMap;
    }

    @Override
    public SeckillSkuDO findById(Long seckillId, Long userId) {
        SeckillSkuDO seckillSkuDO = null;
        try {
            seckillSkuDO = seckillSkuMapper.selectById(seckillId);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return seckillSkuDO;
    }

    @Override
    public Exposer exportSeckillUrl(Long seckillId, Long userId) {
        String md5 = "";
        try {
            SeckillSpuDO seckill = (SeckillSpuDO) redisTemplate.boundHashOps(key).get(seckillId);
            if (seckill == null) {
                //说明redis缓存中没有此key对应的value
                //查询数据库，并将数据放入缓存中
                seckill = seckillSpuMapper.selectById(seckillId);
                if (seckill == null) {
                    //说明没有查询到
                    return new Exposer(false, seckillId);
                } else {
                    //查询到了，存入redis缓存中。 key:秒杀表的ID值； value:秒杀表数据
                    redisTemplate.boundHashOps(key).put(seckill.getId(), seckill);
                    logger.info("RedisTemplate -> 从数据库中读取并放入缓存中");
                }
            } else {
                logger.info("RedisTemplate -> 从缓存中读取");
            }
            Date startTime = seckill.getGmtStart();
            Date endTime = seckill.getGmtEnd();
            //获取系统时间
            Date nowTime = new Date();
            if (nowTime.getTime() < startTime.getTime() || nowTime.getTime() > endTime.getTime()) {
                return new Exposer(false, seckillId, nowTime.getTime(), startTime.getTime(), endTime.getTime());
            }
            //转换特定字符串的过程，不可逆的算法
            md5 = getMD5(seckillId);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return new Exposer(true, md5, seckillId);
    }

    //生成MD5值
    private String getMD5(Long seckillId) {
        String base = seckillId + "/" + salt;
        String md5 = DigestUtils.md5DigestAsHex(base.getBytes());
        return md5;
    }

    @Override
    public SeckillResult<SeckillExecution> executeSeckill(Long seckillId, String md5,
                                                          Integer money, Long userId) {
        try {
            SeckillExecution execution = this.execute(seckillId, money, userId, md5);
            return new SeckillResult<SeckillExecution>(true, execution);
        } catch (RepeatKillException e) {
            SeckillExecution seckillExecution = new SeckillExecution(seckillId, SeckillStatEnum.REPEAT_KILL);
            return new SeckillResult<SeckillExecution>(true, seckillExecution);
        } catch (SeckillCloseException e) {
            SeckillExecution seckillExecution = new SeckillExecution(seckillId, SeckillStatEnum.END);
            return new SeckillResult<SeckillExecution>(true, seckillExecution);
        } catch (SeckillException e) {
            SeckillExecution seckillExecution = new SeckillExecution(seckillId, SeckillStatEnum.INNER_ERROR);
            return new SeckillResult<SeckillExecution>(true, seckillExecution);
        }
    }

    /**
     * 使用注解式事务方法的有优点：开发团队达成了一致约定，明确标注事务方法的编程风格
     * 使用事务控制需要注意：
     * 1.保证事务方法的执行时间尽可能短，不要穿插其他网络操作PRC/HTTP请求（可以将这些请求剥离出来）
     * 2.不是所有的方法都需要事务控制，如只有一条修改的操作、只读操作等是不需要进行事务控制的
     * <p>
     * Spring默认只对运行期异常进行事务的回滚操作，对于编译异常Spring是不进行回滚的，所以对于需要进行事务控制的方法尽可能将可能抛出的异常都转换成运行期异常
     */
    @Transactional
    public SeckillExecution execute(Long seckillId, Integer money, Long userId, String md5)
            throws SeckillException, RepeatKillException, SeckillCloseException {
        if (md5 == null || !md5.equals(getMD5(seckillId))) {
            throw new SeckillException("seckill data rewrite");
        }
        //执行秒杀逻辑：1.减库存；2.储存秒杀订单
        Date nowTime = new Date();

        try {
            //记录秒杀订单信息
            int insertCount = seckillOrderMapper.insertOrder(seckillId, money, userId);
            //唯一性：seckillId,userPhone，保证一个用户只能秒杀一件商品
            if (insertCount <= 0) {
                //重复秒杀
                throw new RepeatKillException("seckill repeated");
            } else {
                //减库存
                int updateCount = seckillSpuMapper.reduceStock(seckillId, nowTime);
                if (updateCount <= 0) {
                    //没有更新记录，秒杀结束
                    throw new SeckillCloseException("seckill is closed");
                } else {
                    //秒杀成功
                    SeckillOrderDO seckillOrder = seckillOrderMapper.findById(seckillId, userId);
                    //更新缓存（更新库存数量）
                    SeckillSpuDTO seckillSpuDTO = (SeckillSpuDTO) redisTemplate.boundHashOps(key).get(seckillId);
                    seckillSpuDTO.setStockCount(seckillSpuDTO.getStockCount() - 1);
                    redisTemplate.boundHashOps(key).put(seckillId, seckillSpuDTO);
                    return new SeckillExecution(seckillId, SeckillStatEnum.SUCCESS, seckillOrder);
                }
            }
        } catch (SeckillCloseException e) {
//            throw e;
            e.printStackTrace();
        } catch (RepeatKillException e) {
//            throw e;
            e.printStackTrace();
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
            //所有编译期异常，转换为运行期异常
//            throw new SeckillException("seckill inner error:" + e.getMessage());
            e.printStackTrace();
        }
        return null;
    }

    private void packSpuCollectInfo(SpuDTO spuDTO, Long userId) throws ServiceException {
        if (userId != null) {
            Boolean collectStatus = collectBizService.getCollectBySpuId(spuDTO.getId(), userId);
            spuDTO.setCollect(collectStatus);
        }
    }

    public SeckillSpuPlusDTO getGoods(Long spuId, Long seckillSpuId, Long userId) throws ServiceException {

        SpuDO spuDO = spuMapper.selectById(spuId);
        SeckillSpuPlusDTO seckillSpuPlusDTO = new SeckillSpuPlusDTO();

        seckillSpuPlusDTO.setActivityType(OrderActivityType.ORDERACTIVITYTYPESECKILL.getCode());
        BeanUtils.copyProperties(spuDO, seckillSpuPlusDTO);
        seckillSpuPlusDTO.setImgList(imgMapper.getImgs(BizType.GOODS.getCode(), spuId));
        List<SeckillSkuDTO> seckillSkuDTOS = seckillSkuMapper.selectSeckillSkuList(seckillSpuId);
        //根据user购买量限制  用户购买数量
        for (SeckillSkuDTO seckillSkuDTO : seckillSkuDTOS
        ) {
            // 添加团长
            Wrapper<SkuColonelDO> wrappera = new EntityWrapper<SkuColonelDO>();
            if (!StringUtils.isEmpty(seckillSkuDTO.getBarCode())) {
                wrappera.eq("bar_code", seckillSkuDTO.getBarCode());
            }
            List<SkuColonelDO> skuColonelDOList = skuColonelMapper.selectList(wrappera);
            seckillSkuDTO.setColonelDOList(skuColonelDOList);
            //设置限购
            SeckillUserSkuDTO seckillUserSkuDTO = seckillUserSkuMapper.checkUserSeckillCount(userId, seckillSkuDTO.getId());
            if (seckillUserSkuDTO == null) {
                continue;
            }
            seckillSkuDTO.setSkuLimitCount(seckillSkuDTO.getSkuLimitCount() - seckillUserSkuDTO.getBuyCount());
        }

        SeckillSpuDO seckillSpuDO = seckillSpuMapper.selectById(seckillSpuId);
        if (seckillSpuDO == null) {
            throw new AdminServiceException(ExceptionDefinition.SECKILL_SPU_NO_EXITS);
        }
        SeckillInfo seckillInfo = new SeckillInfo();
        seckillInfo.setGmtStart(seckillSpuDO.getGmtStart());
        seckillInfo.setGmtEnd(seckillSpuDO.getGmtEnd());
        seckillInfo.setSeckillStatus(seckillSpuDO.getStatus());
        seckillInfo.setSpuSeckillPrice(seckillSpuDO.getSpuSeckillPrice());
        seckillInfo.setSpuId(spuId);
        seckillInfo.setSeckillSpuId(seckillSpuId);
        seckillInfo.setSpuStockCount(seckillSpuDO.getStockCount());
        seckillSpuPlusDTO.setSeckillInfo(seckillInfo);
        seckillSpuPlusDTO.setSeckillSkuList(seckillSkuDTOS);
        List<SkuDO> skuDOList = skuMapper.selectList(
                new EntityWrapper<SkuDO>()
                        .eq("spu_id", spuId));
        //根据skuid查询信息并放回到sku中

        for (SkuDO skuDO : skuDOList) {
            Wrapper<SkuColonelDO> wrappera = new EntityWrapper<SkuColonelDO>();
            if (!StringUtils.isEmpty(skuDO.getBarCode())) {
                wrappera.eq("bar_code", skuDO.getBarCode());
            }
            List<SkuColonelDO> skuColonelDOList = skuColonelMapper.selectList(wrappera);
        }
        seckillSpuPlusDTO.setSkuList(skuDOList);

        //类目族
        seckillSpuPlusDTO.setCategoryIds(categoryBizService.getCategoryFamily(spuDO.getCategoryId()));
        Double sum = skuDOList.stream().mapToDouble(item -> item.getStock()).sum();
        Float floatSum = sum == null ? 0.00f : sum.floatValue();
        seckillSpuPlusDTO.setStock(floatSum);
        //获取商品属性
        List<SpuAttributeDO> spuAttributeList = spuAttributeMapper.selectList(new EntityWrapper<SpuAttributeDO>().eq("spu_id", spuId));
        seckillSpuPlusDTO.setAttributeList(spuAttributeList);
        //获取运费模板
        FreightTemplateDTO templateDTO = freightBizService.getTemplateById(spuDO.getFreightTemplateId());
        seckillSpuPlusDTO.setFreightTemplate(templateDTO);
        //放入缓存
        cacheComponent.putObj(SECKILL_SPU_PREFIX + spuId, seckillSpuPlusDTO, Const.CACHE_ONE_DAY / 2);
        return seckillSpuPlusDTO;
    }

    @Override
    public SeckillSpuPlusDTO getSeckillSkuList(Long seckillSpuId, Long userId) throws ServiceException {
        SeckillSpuDO seckillSpuDO = seckillSpuMapper.selectById(seckillSpuId);
        if (seckillSpuDO == null) {
            throw new AdminServiceException(ExceptionDefinition.SECKILL_SPUID_NOEXIST);
        }
        SeckillSpuPlusDTO seckillSpuPlusDTO = new SeckillSpuPlusDTO();
        try {
            Long spuId = seckillSpuDO.getSpuId();
            seckillSpuPlusDTO = getGoods(spuId, seckillSpuId, userId);
            seckillSpuPlusDTO.setActivityType(CollectType.COLLECTTYPESECKILL.getCode());
            CollectDO collectDO = collectMapper.selectCollectOne(spuId, userId);
            if (collectDO != null) {
                seckillSpuPlusDTO.setCollect(true);
            }
        } catch (ServiceException e) {
            e.printStackTrace();
        }
        return seckillSpuPlusDTO;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public String takeSeckillOrder(OrderRequestDTO orderRequest, String channel, Long userId) throws ServiceException {
        if (lockComponent.tryLock(TAKE_SECKILLORDER_LOCK + userId, 20)) {
            //加上乐观锁，防止用户重复提交订单
            try {
                //用户会员等级
                Integer userLevel = SessionUtil.getUser().getLevel();
                OrderDO orderDO = new OrderDO();
                Long colonelLevelId = null;
                Long colonelStoreId = 0L;
                //参数强校验 START
                List<OrderRequestSkuDTO> skuList = orderRequest.getSkuList();
                if (CollectionUtils.isEmpty(skuList) || orderRequest.getTotalPrice() == null) {
                    throw new AppServiceException(ExceptionDefinition.PARAM_CHECK_FAILED);
                }
                if (orderRequest.getTotalPrice() <= 0) {
                    throw new AppServiceException(ExceptionDefinition.ORDER_PRICE_MUST_GT_ZERO);
                }
                if (orderRequest.getAddressId() == null) {
                    throw new AppServiceException(ExceptionDefinition.ORDER_ADDRESS_CANNOT_BE_NULL);
                }

                /**/
                /* 秒杀判断*/
                /**/

                SeckillSpuDO seckillSpuDO = seckillSpuMapper.selectById(orderRequest.getSeckillId());
                if (orderRequest.getSeckillId() != null) {
                    //校验团购参数
                    if (skuList.size() > 1) {
                        throw new AppServiceException(ExceptionDefinition.ORDER_SECKILL_SPU_CAN_SINGLE_TAKE);
                    }

                    // 判断是否在秒杀中
                    if (seckillSpuDO == null || seckillSpuDO.getStatus() != SeckillStatusType.SECKILLING.getCode()) {
                        throw new AppServiceException(ExceptionDefinition.SECKILL_SPU_NO_EXITS);
                    }
                }
                //商品价格
                int skuPrice = 0;
                int skuOriginalPrice = 0;
                //稍后用于优惠券作用范围校验
                Map<Long, Integer> categoryPriceMap = new HashMap<>();
                //稍后用于插入OrderSku
                Map<Long, SeckillSkuDO> skuIdDTOMap = new HashMap<>();
                List<SeckillSkuDO> stockNotEnoughList = new ArrayList<>();
                List<SeckillSkuDO> limitList = new ArrayList<>();
                List<SeckillSkuDO> statusList = new ArrayList<>();
                Integer levelMoney = new Integer(0);
                for (OrderRequestSkuDTO orderRequestSkuDTO : skuList) {

                    SkuDO skuDO = skuMapper.selectById(orderRequestSkuDTO.getSkuId());
                    SpuDO spuDO = spuMapper.selectById(skuDO.getSpuId());

                    //取数据从  秒杀表中取
                    SeckillSkuDO seckillSkuDO = seckillSkuMapper.selectById(orderRequestSkuDTO.getSeckillSkuId());

                    skuIdDTOMap.put(seckillSkuDO.getId(), seckillSkuDO);
                    if (seckillSkuDO == null) {
                        throw new AppServiceException(ExceptionDefinition.ORDER_SKU_NOT_EXIST);
                    }
                    //判断限制数量
                    if (seckillSkuDO.getSkuLimitCount() != null && seckillSkuDO.getSkuLimitCount() > 0 && seckillSkuDO.getSkuLimitCount() < orderRequestSkuDTO.getNum()) {
                        // throw new AppServiceException(ExceptionDefinition.ORDER_SKU_STOCK_NOT_ENOUGH);
                        limitList.add(seckillSkuDO);
                    }
                    //判断下架产品  (秒杀 未加下架状态)

                    //判断秒杀库存量
                    if (seckillSkuDO.getStockCount() < orderRequestSkuDTO.getNum()) {
                        // throw new AppServiceException(ExceptionDefinition.ORDER_SKU_STOCK_NOT_ENOUGH);
                        stockNotEnoughList.add(seckillSkuDO);
                    } else {
                        int p;
                        p = (int) (seckillSkuDO.getSkuSeckillPrice() * orderRequestSkuDTO.getNum());

                        skuPrice += p;
                        skuOriginalPrice += skuDO.getOriginalPrice() * orderRequestSkuDTO.getNum();

//                        skuOriginalPrice += skuDTO.getOriginalPrice() * orderRequestSkuDTO.getNum();
//                        List<Long> categoryFamily = categoryService.getCategoryFamily(skuDTO.getCategoryId());
//                        for (Long cid : categoryFamily) {
//                            Integer price = categoryPriceMap.get(cid);
//                            if (price == null) {
//                                price = p;
//                            } else {
//                                price += p;
//                            }
//                            categoryPriceMap.put(cid, price);
//                        }
                    }
                }
                //某些产品已经下架
//                if (!CollectionUtils.isEmpty(statusList)) {
//                    // 在后端提取
//                    StringBuilder sb = new StringBuilder();
//                    for (int i = 0; i < statusList.size(); i++) {
//                        SkuDTO skuDTO = statusList.get(i);
//                        sb.append(skuDTO.getSpuTitle());
//                        sb.append(" ");
//                        sb.append(skuDTO.getTitle());
//                        sb.append(" ");
//                        sb.append("已下架");
//                    }
//                    throw new AppServiceException(sb.toString(), ExceptionDefinition.ORDER_SKU_XIAJIA.getCode());
//                }
                //限制购买数量
                if (!CollectionUtils.isEmpty(limitList)) {
                    // 在后端提取
                    StringBuilder sb = new StringBuilder();
                    for (int i = 0; i < limitList.size(); i++) {
                        SeckillSkuDO seckillSkuDO = limitList.get(i);
                        sb.append(seckillSpuDO.getSpuTitle());
                        sb.append(" ");
                        sb.append(seckillSkuDO.getSkuTitle());
                        sb.append(" ");
                        sb.append("限制最多只能购买" + seckillSpuDO.getLimitCount());

                    }
                    throw new AppServiceException(sb.toString(), ExceptionDefinition.ORDER_SKU_LIMIT.getCode());
                }
                if (!CollectionUtils.isEmpty(stockNotEnoughList)) {
                    // 在后端提取
                    StringBuilder sb = new StringBuilder();
                    for (int i = 0; i < stockNotEnoughList.size(); i++) {
                        SeckillSkuDO seckillSkuDO = stockNotEnoughList.get(i);
                        sb.append(seckillSpuDO.getSpuTitle());
                        sb.append(" ");
                        sb.append(seckillSkuDO.getSkuTitle());
                        sb.append("\r\n");
                    }
                    sb.append("库存不足");
                    throw new AppServiceException(sb.toString(), ExceptionDefinition.ORDER_SKU_STOCK_NOT_ENOUGH_V2.getCode());
                }

                if (skuPrice != orderRequest.getTotalPrice()) {
                    throw new AppServiceException(ExceptionDefinition.ORDER_PRICE_CHECK_FAILED);
                }

                //优惠券折扣价格
                int couponPrice = 0;
                //优惠券校验
//                UserCouponDTO userCouponFromFront = orderRequest.getCoupon();
//                if (userCouponFromFront != null) {
//                    if (userCouponFromFront.getId() == null || userCouponFromFront.getDiscount() == null) {
//                        throw new AppServiceException(ExceptionDefinition.PARAM_CHECK_FAILED);
//                    }
//
//                    UserCouponDTO userCouponFromDB = userCouponMapper.getUserCouponById(userCouponFromFront.getId(), userId);
//
//                    if (userCouponFromDB == null) {
//                        throw new AppServiceException(ExceptionDefinition.ORDER_COUPON_NOT_EXIST);
//                    }
//
//                    if (!userCouponFromDB.getDiscount().equals(userCouponFromFront.getDiscount())) {
//                        throw new AppServiceException(ExceptionDefinition.ORDER_COUPON_DISCOUNT_CHECK_FAILED);
//                    }
//
//                    //校验优惠券策略是否满足
//                    Long categoryId = userCouponFromDB.getCategoryId();
//                    if (categoryId != null) {
//                        Integer p = categoryPriceMap.get(categoryId);
//                        if (p < userCouponFromDB.getMin()) {
//                            throw new AppServiceException(ExceptionDefinition.ORDER_COUPON_PRICE_NOT_ENOUGH);
//                        }
//                    } else {
//                        if (skuPrice < userCouponFromDB.getMin()) {
//                            throw new AppServiceException(ExceptionDefinition.ORDER_COUPON_PRICE_NOT_ENOUGH);
//                        }
//                    }
//                    couponPrice = userCouponFromDB.getDiscount();
//                }
                // 快递费计算
                Integer freightPrice = freightBizService.getFreightMoney(orderRequest);
                //参数强校验 END
                //???是否校验actualPrice??强迫校验？
                Date now = new Date();

                String orderNo = GeneratorUtil.genOrderId(MACHINE_NO, ENV);

                int actualPrice = skuPrice - couponPrice + freightPrice ;

                orderDO.setSkuTotalPrice(skuPrice);
                orderDO.setSkuOriginalTotalPrice(skuOriginalPrice);
                orderDO.setChannel(channel);
                orderDO.setActualPrice(actualPrice);
                if (couponPrice != 0) {
                    orderDO.setCouponId(orderRequest.getCoupon().getCouponId());
                    orderDO.setCouponPrice(couponPrice);
                }
                orderDO.setMono(orderRequest.getMono());
                orderDO.setFreightPrice(freightPrice);
                orderDO.setSkuOriginalTotalPrice(skuOriginalPrice);
                orderDO.setOrderNo(orderNo);
                orderDO.setUserId(userId);
                orderDO.setStatus(OrderStatusType.UNPAY.getCode());
                orderDO.setGmtUpdate(now);
                orderDO.setGmtCreate(now);
                orderDO.setAvtiveType(OrderActivityType.ORDERACTIVITYTYPESECKILL.getCode());
                orderDO.setGroupShopId(seckillSpuDO.getId());
                orderDO.setBuyType(seckillSpuDO.getBuyType());
                if (orderRequest.getAddressId() != null) {
                    AddressDO addressDO = addressMapper.selectById(orderRequest.getAddressId());
                    if (!userId.equals(addressDO.getUserId())) {
                        throw new AppServiceException(ExceptionDefinition.ORDER_ADDRESS_NOT_BELONGS_TO_YOU);
                    }
                    orderDO.setConsignee(addressDO.getConsignee());
                    orderDO.setPhone(addressDO.getPhone());
                    orderDO.setProvince(addressDO.getProvince());
                    orderDO.setCity(addressDO.getCity());
                    orderDO.setCounty(addressDO.getCounty());
                    orderDO.setAddress(addressDO.getAddress());
                }
                orderMapper.insert(orderDO);

                //扣除用户优惠券
                if (orderDO.getCouponId() != null) {
                    UserCouponDO updateUserCouponDO = new UserCouponDO();
                    updateUserCouponDO.setId(orderDO.getCouponId());
                    updateUserCouponDO.setGmtUsed(now);
                    updateUserCouponDO.setOrderId(orderDO.getId());
                    userCouponMapper.updateById(updateUserCouponDO);
                }

                //插入OrderSku
                Long finalColonelLevelId = colonelLevelId;
                skuList.forEach(item -> {
                    SeckillSkuDO seckillSkuDO = skuIdDTOMap.get(item.getSeckillSkuId());
                    OrderSkuDO orderSkuDO = new OrderSkuDO();
                    orderSkuDO.setBarCode(seckillSkuDO.getBarCode());
                    orderSkuDO.setTitle(seckillSkuDO.getSkuTitle());
                    SkuDO skuDO = skuMapper.selectById(seckillSkuDO.getSkuId());
                    SpuDO spuDO = spuMapper.selectById(skuDO.getSpuId());
                    orderSkuDO.setUnit(spuDO.getUnit());
                    orderSkuDO.setSpuTitle(seckillSpuDO.getSpuTitle());
                    orderSkuDO.setImg(skuDO.getImg() == null ? spuDO.getImg() : skuDO.getImg());
                    orderSkuDO.setNum(item.getNum().intValue());
                    orderSkuDO.setOriginalPrice(skuDO.getOriginalPrice());
                    orderSkuDO.setPrice(seckillSkuDO.getSkuSeckillPrice());

                    orderSkuDO.setSkuId(seckillSkuDO.getSkuId());
                    orderSkuDO.setSpuId(seckillSkuDO.getSpuId());
                    orderSkuDO.setOrderNo(orderDO.getOrderNo());
                    orderSkuDO.setOrderId(orderDO.getId());
                    orderSkuDO.setGmtCreate(now);
                    orderSkuDO.setGmtUpdate(now);

                    //创建秒杀订单关联
                    SeckillOrderSkuDO seckillOrderSkuDO = new SeckillOrderSkuDO();
                    seckillOrderSkuDO.setSeckillOrderNo(orderDO.getOrderNo());
                    seckillOrderSkuDO.setSeckillSkuId(seckillSkuDO.getId());
                    seckillOrderSkuDO.setGmtCreate(now);
                    seckillOrderSkuDO.setGmtUpdate(now);

                    //扣除库存    扣除秒杀库存
                    SeckillSkuDO seckillSkuDOoperation = new SeckillSkuDO();
                    SeckillSpuDO tmp=new SeckillSpuDO();
                    try {
                        seckillSkuDOoperation = SeckillSkuBizService.selectByskuIdAndseckillSpuIdAndUpdate(seckillSpuDO.getId(), item);
                        tmp= seckillSpuBizService.selectByUpdateSeckillSpuDO(seckillSpuDO.getId(), item);
                    } catch (Exception e) {
                        doThrow(e);
                    }

                    Integer checkDB = 0;
                    checkDB += orderSkuMapper.insert(orderSkuDO);
                    checkDB += seckillOrderSkuMapper.insert(seckillOrderSkuDO);

                    checkDB += seckillSkuMapper.updateById(seckillSkuDOoperation);
                               seckillSpuMapper.updateById(tmp);
                });
                if (!StringUtils.isEmpty(orderRequest.getTakeWay())) {
                    String takeWay = orderRequest.getTakeWay();
                    if ("cart".equals(takeWay)) {
                        //扣除购物车
                        List<Long> skuIds = skuList.stream().map(item -> item.getSkuId()).collect(Collectors.toList());
                        cartMapper.delete(new EntityWrapper<CartDO>().in("sku_id", skuIds).eq("user_id", userId));
                    }
                    //直接购买传值为 "buy"
                }
                return orderDO.getOrderNo();

            } catch (ServiceException e) {
                throw e;
            } catch (Exception e) {
                logger.error("[提交订单] 异常", e);
                throw new AppServiceException(ExceptionDefinition.ORDER_UNKNOWN_EXCEPTION);
            } finally {
                lockComponent.release(TAKE_SECKILLORDER_LOCK + userId);
            }
        }
        throw new AppServiceException(ExceptionDefinition.ORDER_SYSTEM_BUSY);
    }


}
